﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using PSE.Deployment.Engine;
using PSE.Deployment.Engine.CustomEventArgs;
using PSE.Deployment.Engine.Enums;
using PSE.Deployment.Engine.Interfaces;
using PSE.Deployment.SetupBase.Parameter.Enums;
using PSE.Deployment.SetupBase.UI;
using PSE.Deployment.Util;
using PSE.Deployment.SetupBase.Configuration;

namespace PSE.Deployment.SetupBase.Groups
{
    public abstract class SetupExecutionGroup : BaseSetupGroup
    {
        #region Attributes

        private Thread _windowProgressThread;
        private ManualResetEvent _windowOpened;
        private InstallationProgress _progress;

        #endregion

        #region Constructor

        public SetupExecutionGroup(IStepGroup parent)
            : base(parent)
        {
            this.ExecutionStarted += new EventHandler<ExecutionStartedEventArgs>(SetupExecutionGroup_ExecutionStarted);
            this.ExecutionProgress += new EventHandler<ExecutionProgressEventArgs>(SetupExecutionGroup_ExecutionProgress);
            this.ExecutionFinished += new EventHandler<ExecutionFinishedEventArgs>(SetupExecutionGroup_ExecutionFinished);
            this.ExecutionCanceled += new EventHandler<ExecutionCanceledEventArgs>(SetupExecutionGroup_ExecutionCanceled);
            this.ExecutionError += new EventHandler<ExecutionErrorEventArgs>(SetupExecutionGroup_ExecutionError);

            this.CancelBehavior = StepGroupCancelBehavior.CancelParent;

            this.UndoStarted += new EventHandler<UndoStartedEventArgs>(SetupExecutionGroup_UndoStarted);
            this.UndoFinished += new EventHandler<UndoFinishedEventArgs>(SetupExecutionGroup_UndoFinished);

            _windowOpened = new ManualResetEvent(false);
        }

        #endregion

        #region Undo

        public override void Undo()
        {
            base.Undo();

            if (_progress != null)
            {
                _progress.Close(false);
                _windowProgressThread.Join();
            }
        }

        #endregion

        #region Execution Control

        protected override void AddStepsExecution()
        {
            base.AddStepsExecution();
        }

        #endregion

        #region Group Events

        private void SetupExecutionGroup_ExecutionStarted(object sender, ExecutionStartedEventArgs e)
        {
            //PSE.Deployment.Util.Serializer.Serialize(SetupMainFactory.GetInstance().Parameter.GetType(), SetupMainFactory.GetInstance().Parameter, @"c:\param.xml");

            if (!SetupMainFactory.GetInstance().Parameter.ShowWizard)
                return;

            if (e.OriginalSource == this)
            {
                _windowProgressThread = new Thread(OpenWindow);
                _windowProgressThread.SetApartmentState(ApartmentState.STA);
                _windowProgressThread.Start();

                while (!_windowOpened.WaitOne(10)) ;

                _windowOpened.Reset();

                int count = SumMaximumProgress(this);

                _progress.ResetTotalProgress(count);

                _progress.UpdateStepDescription(string.Empty);
            }
            else
            {
                int count = SumMaximumProgress(this);

                _progress.ResetTotalProgress(count);

                if (!(e.OriginalSource is IStepGroup))
                {
                    if (((IStep)e.OriginalSource).Parent is SetupExecutionGroup)
                    {
                        _progress.UpdateGroupDescription(e.OriginalSource.Description);
                    }

                    _progress.UpdateStepDescription(e.OriginalSource.Description);
                    _progress.ResetStepProgress(e.OriginalSource.MaximumProgress);
                }
                else
                {
                    if (((IStepGroup)e.OriginalSource).Steps.Count > 0)
                    {
                        _progress.UpdateGroupDescription(e.OriginalSource.Description);
                    }
                }
            }

            _progress.AllowClose = true;
            _progress.AllowCancel = true;
        }

        private void SetupExecutionGroup_UndoStarted(object sender, UndoStartedEventArgs e)
        {
            if (!SetupMainFactory.GetInstance().Parameter.ShowWizard)
                return;

            if (!(e.OriginalSource is IStepGroup))
            {
                _progress.UpdateStepDescription(string.Format("Desfazendo: {0}", e.OriginalSource.Description));
                _progress.ResetStepProgress(e.OriginalSource.MaximumProgress);
                _progress.IncrementStepProgress(e.OriginalSource.CurrentProgress, string.Empty);

                //System.Threading.Thread.Sleep(200);
            }
            else
            {
                if (((IStepGroup)e.OriginalSource).Steps.Count > 0)
                {
                    _progress.UpdateGroupDescription(e.OriginalSource.Description);
                }
            }
        }

        private void SetupExecutionGroup_UndoFinished(object sender, UndoFinishedEventArgs e)
        {
            if (e.OriginalSource == this)
            {
                //Thread.Sleep(1000);

                SetupMainFactory.GetInstance().ContinueExecution();
            }
        }

        private void SetupExecutionGroup_ExecutionProgress(object sender, ExecutionProgressEventArgs e)
        {
            if (!SetupMainFactory.GetInstance().Parameter.ShowWizard)
                return;

            if (e.CurrentProgress > 0)
            {
                if (e.OriginalSource.ExecutionStatus == StepExecutionStatus.Finished)
                {
                    _progress.IncrementTotalProgress();
                }
            }

            if (e.OriginalSource.ExecutionStatus != StepExecutionStatus.Finished)
            {
                _progress.DecrementTotalProgress();
            }

            _progress.IncrementStepProgress(e.CurrentProgress, e.ProgressDescription);

            //System.Threading.Thread.Sleep(200);
        }

        private void SetupExecutionGroup_ExecutionFinished(object sender, ExecutionFinishedEventArgs e)
        {
            if (!SetupMainFactory.GetInstance().Parameter.ShowWizard)
                return;

            if (e.OriginalSource == this)
            {
                //Thread.Sleep(1000);
                _progress.Close(false);
                _windowProgressThread.Join();

                if (SetupMainFactory.GetInstance().Parameter.ShowResult)
                {
                    ShowSetupResult();

                    ShowInstallationReport();
                }
            }
            else
            {
                if (_progress.NavigationAction.GetValueOrDefault() == StepNavigationAction.Cancel)
                {
                    if (SetupMainFactory.GetInstance().Parameter.IsCancelling)
                    {
                        e.NavigationAction = StepNavigationAction.Cancel;
                    }
                    else
                    {
                        e.NavigationAction = StepNavigationAction.Next;
                    }
                }
            }
        }

        private void SetupExecutionGroup_ExecutionError(object sender, ExecutionErrorEventArgs e)
        {
            if (!SetupMainFactory.GetInstance().Parameter.ShowWizard)
                return;

            if (e.OriginalSource.ErrorBehavior == StepErrorBehavior.NotifyParent)
            {
                e.ErrorAction = _progress.ShowExecutionError(e.Exception, e.OriginalSource.Ignorable);
                if (e.ErrorAction == StepErrorAction.CancelParent)
                    SetupMainFactory.GetInstance().Parameter.IsCancelling = true;
            }
        }

        private void SetupExecutionGroup_ExecutionCanceled(object sender, ExecutionCanceledEventArgs e)
        {

        }

        #endregion

        #region Result Information

        private void ShowSetupResult()
        {
            List<IStepGroup> groups = new List<IStepGroup>();

            foreach (IStep step in this.Steps)
            {
                IStepGroup group = step as IStepGroup;

                if (group != null && group.Steps.Count > 0)
                {
                    groups.Add(group);
                }
            }

            if (groups.Count > 0)
            {
                SetupResult result = new SetupResult();
                result.SetupMode = SetupMainFactory.GetInstance().Parameter.SetupMode;
                result.StepGroups = groups;
                result.ShowDialog();
            }
        }

        private void ShowInstallationReport()
        {
            StepCollection ignoredSteps = new StepCollection();

            foreach (IStep stepGroup in this.Steps)
            {
                if (stepGroup is IStepGroup)
                {
                    foreach (IStep step in ((IStepGroup)stepGroup).IgnoredSteps)
                    {
                        ignoredSteps.Add(step);
                    }
                }
            }

            foreach (IStep step in this.IgnoredSteps)
            {
                ignoredSteps.Add(step);
            }

            if (ignoredSteps.Count > 0)
            {
                InstallationReport report = new InstallationReport();
                report.IgnoredSteps = ignoredSteps;
                report.SetupMode = SetupMainFactory.GetInstance().Parameter.SetupMode;
                report.ShowDialog();
            }
        }

        #endregion

        #region Installation Progress

        private void OpenWindow()
        {
            _progress = new InstallationProgress();

            _progress.ExecutionContinue += new Action(_progress_ExecutionContinue);
            _progress.ExecutionPause += new Action(_progress_ExecutionPause);
            _progress.ExecutionStop += new Action(_progress_ExecutionStop);

            if (SetupMainFactory.GetInstance().Parameter.SetupMode == PSE.Deployment.SetupBase.Parameter.Enums.SetupMode.Uninstall)
                _progress.UpdateConfirmOnClosingMessage("\n\nPodem restar componentes do Sistema e não será possível executá-lo corretamente.");

            _progress.Show();
            _windowOpened.Set();
            _progress.RunDispatcher();
        }

        private void _progress_ExecutionStop()
        {
            SetupMainFactory.GetInstance().StopExecution();
        }

        private void _progress_ExecutionPause()
        {
            SetupMainFactory.GetInstance().Parameter.IsCancelling = true;

            SetupMainFactory.GetInstance().PauseExecution();
        }

        private void _progress_ExecutionContinue()
        {
            SetupMainFactory.GetInstance().ContinueExecution();
        }

        private int SumMaximumProgress(IStepGroup parent)
        {
            int maximumProgress = 0;

            foreach (IStep step in parent.Steps)
            {
                if (step.ExecutionStatus == StepExecutionStatus.Finished)
                {
                    if (step is IStepGroup && ((IStepGroup)step).Steps.Count > 0)
                    {
                        maximumProgress += SumMaximumProgress((IStepGroup)step);
                    }
                    else
                    {
                        maximumProgress += step.MaximumProgress;
                    }
                }
            }

            return maximumProgress;
        }

        #endregion
    }
}